package com.hww.v2_mtfsdk;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.hww.common.MyAppThis;
import com.hww.common.util.ImageUtils;
import com.hww.common.util.LogUtils;
import com.hww.common.util.SharePreferencesUtils;
import com.hww.common.util.ThreadUtils;
import com.hww.v2_mtfsdk.utils.camera.Camera1TextureView;
import com.hww.v2_mtfsdk.utils.camera.CameraCallBack;
import com.hww.v2_mtfsdk.utils.camera.FaceMarkSurface;

import net.facelib.live.FaceLiveAndroid;
import net.facelib.mtfsdk.FaceApiMtfV2Android;
import net.facelib.mtfsdk.FseMtfAndroidBridge;
import net.gdface.image.MatType;
import net.gdface.sdk.CodeInfo;
import net.gdface.sdk.FRect;
import net.gdface.sdk.NotFaceDetectedException;
import net.gdface.sdk.fse.CodeBean;
import net.gdface.sdk.fse.FeatureSe;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;

import gu.jimgutil.MatrixUtils;

/**
 * @author hww
 * @date 0011 2024/7/11
 * email: heww@zhongyuntech.cn
 * description:
 */
public class FaceActivity extends Activity {

    private byte[] feature;

    private synchronized byte[] getFeature() {
        return feature;
    }

    private synchronized void setFeature(byte[] feature) {
        this.feature = feature;
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);//这行代码一定要在setContentView之前，不然会闪退
        Window window = getWindow();
        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_face_test);
        LogUtils.file("activity", "onCreate()\n\n\n");

        cameraId = SharePreferencesUtils.getInt(MyAppThis.getThis(), Constant.SHARE_CAMERA_ID, 0);
        cameraAnglePreview = SharePreferencesUtils.getInt(MyAppThis.getThis(), Constant.SHARE_ANGLE_PREVIEW, 0);
        cameraAngleCall = SharePreferencesUtils.getInt(MyAppThis.getThis(), Constant.SHARE_ANGLE_CALL, 0);
        isMirroring = SharePreferencesUtils.getBoolean(MyAppThis.getThis(), Constant.SHARE_FACE_MIRRORING, false);
        similarityThreshold = (float) SharePreferencesUtils.get(MyAppThis.getThis(), Constant.SHARE_FACE_SIMILARITY, 0.65f);

        LogUtils.file("FaceActivity", String.format(
                "设备打开摄像头情况：  cameraId=%s   width=%s    height=%s   previewAngle=%s   callBackAngle=%s"
                , cameraId, cameraWidth, cameraHeight, cameraAnglePreview, cameraAngleCall));
        initView();
    }

    @Override
    protected void onResume() {
        super.onResume();
        timeRecognition = System.currentTimeMillis();
        tvCountDown = findViewById(R.id.test_tv_count_down);
        sumCountDown = 30;
        startPreview();
    }

    private TextView tvCountDown;
    private int sumCountDown = 30;

    @Override
    protected void onStop() {
        super.onStop();
        if (null != handler) handler.removeMessages(LABEL_COUNT_DOWN);
        LogUtils.file("activity", "onStop()");
        if (null != camera1TextureView) {
            camera1TextureView.stopPreview();
            camera1TextureView = null;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    private boolean liveStatus = false;
    private int cameraWidth = 640, cameraHeight = 480;
    /* 1,90,270  0,,90,90 */
    private int cameraId = 1, cameraAnglePreview = 90, cameraAngleCall = 90;
    private boolean isMirroring = false;
    private float similarityThreshold = 0.65f;

    private RelativeLayout relativeLayout;
    private Button btLive;
    private TextView tv, tvResult;
    private Camera1TextureView camera1TextureView;
    private FaceMarkSurface faceMarkSurface;

    private int widthB, heightB;
    private boolean skipFrame = true;//跳帧

    private void initView() {
        faceMarkSurface = findViewById(R.id.test_surface_draw);
        tv = findViewById(R.id.test_tv_result);
        tvResult = findViewById(R.id.test_tv_result_show);

        btLive = findViewById(R.id.test_btn_live);
        btLive.setOnClickListener(onClickListener);
        showLiveStatus();
        findViewById(R.id.test_btn_back).setOnClickListener(onClickListener);
        findViewById(R.id.test_btn_local).setOnClickListener(onClickListener);

    }

    private void startPreview() {
        LogUtils.file("activity", "startPreView()");
        camera1TextureView = findViewById(R.id.test_surface_camera);
        camera1TextureView.initCameraParams(cameraId, cameraAnglePreview, cameraCallBack);
        camera1TextureView.setPreViewParams(cameraWidth, cameraHeight);
        camera1TextureView.startPreview();
    }

    private volatile boolean statusReg = true;
    CameraCallBack cameraCallBack = new CameraCallBack() {
        @Override
        public void onPreviewFrameCallBack(byte[] nv21, int width, int height, int orientation) {
            if (statusReg) {
                LogUtils.file("activity", "onPreviewFrameCallBack  successS    length=" + nv21.length);
                statusReg = false;
                handler.sendEmptyMessage(LABEL_COUNT_DOWN);
                handler.removeMessages(LABEL_FINISH);
//                handler.removeMessages(LABEL_TASK_RESTART);
                handler.sendEmptyMessage(LABEL_TASK);
            }

            if (skipFrame) {
                skipFrame = false;
                return;
            } else {
                skipFrame = true;
            }
            detectFace(nv21, width, height);
        }

        @Override
        public void cameraStatus(boolean status, String msg) {
            String format = String.format("cameraStatus: status=%s  msg=%s", status, msg);
            LogUtils.file("activity", "cameraStatus   " + format);
            mLog(format);
        }
    };

    private void detectFace(byte[] nv21, int width, int height) {
        ThreadUtils.getSinglePool().submit(new Runnable() {
            @Override
            public void run() {
                try {
                    byte[] copy = Arrays.copyOf(nv21, nv21.length);
                    widthB = width;
                    heightB = height;
                    int cameraDataRotate = cameraAngleCall;
                    if (90 == cameraDataRotate || 270 == cameraDataRotate) {
                        MatrixUtils.nv21Rotate(copy, width, height, cameraDataRotate);
                        widthB = height;
                        heightB = width;
                    } else if (180 == cameraDataRotate) {
                        MatrixUtils.nv21Rotate(copy, width, height, cameraDataRotate);
                    }

                    try {
                        CodeInfo codeInfo = FaceApiMtfV2Android.getInstance().matDetectMaxFace(MatType.NV21, copy, widthB, heightB);
                        if (null != codeInfo) {
                            FRect pos = codeInfo.getPos();
                            /*画人脸框*/
                            if (null != faceMarkSurface) {
                                Rect rect = new Rect(pos.getLeft(), pos.getTop(), pos.getLeft() + pos.getWidth(), pos.getTop() + pos.getHeight());
                                faceMarkSurface.drawInfo(rect, widthB, heightB, isMirroring);
                            }
                            float ratioW = widthB / 7f;/*取宽的7分之一，可调整比例*/
                            float ratioH = heightB / 7f;/*取高的7分之一，可调整比例*/
                            /*判断人脸左右与边框的距离大于 ratioW ，确保人脸左右方向不会太靠边*/
                            boolean bw = pos.getLeft() > ratioW && (widthB - pos.getLeft() - pos.getWidth()) > ratioW;
                            /*判断人脸上下与边框的距离大于 ratioH ，确保人脸上下方向不会太靠边*/
                            boolean bh = pos.getTop() > ratioH && (heightB - pos.getTop() - pos.getHeight()) > ratioH;
                            if (bw && bh) {
                                featureAndLive(codeInfo, copy, widthB, heightB);
                            }
                        } else {
                            if (null != faceMarkSurface) {
                                faceMarkSurface.clearDrawInfo();
                            }
                        }
                    } catch (NotFaceDetectedException e) {
                        if (null != faceMarkSurface) {
                            faceMarkSurface.clearDrawInfo();
                        }
                    }
                } catch (
                        Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 人脸比对
     *
     * @param codeInfo
     * @param nv21
     * @param width
     * @param height
     * @return true比对成功  false比对失败
     */
    private boolean compareFeature(CodeInfo codeInfo, byte[] nv21, int width, int height) {
        timeFeature = System.currentTimeMillis();
        CodeInfo featureCode = FaceApiMtfV2Android.getInstance().matGetCodeInfo(MatType.NV21, nv21, width, height, codeInfo);
        LogUtils.file("faceSDK", String.format("feature(特征提取结果) = %s     time=%s",
                (null == featureCode ? "false" : "true"), (System.currentTimeMillis() - timeFeature)));
        if (null != featureCode && null != featureCode.getCode() && null != getFeature()) {
            double compareCode = FaceApiMtfV2Android.getInstance().compareCode(getFeature(), featureCode.getCode());
            LogUtils.file("faceSDK", String.format("compare(比对结果) = %s     time = %s", df.format(compareCode), (System.currentTimeMillis() - timeRecognition)));
            mLog("feature == " + compareCode);
            if (compareCode >= similarityThreshold) {
                /*compareStatus = true;*/
                handler.sendEmptyMessageDelayed(LABEL_FINISH, 1000);
                String path = Environment.getExternalStorageDirectory().getPath() + "/dly_demo_log/" + "log-" + System.currentTimeMillis() + ".jpg";
                Bitmap bitmap1 = ImageUtils.nv21ToBitmap(nv21, width, height);
                ImageUtils.save(bitmap1, path, Bitmap.CompressFormat.JPEG, false);
                return true;
            }
        }
        return false;
    }

    private boolean compareFeatureSE(CodeInfo codeInfo, byte[] nv21, int width, int height) {
        FeatureSe featureSe = FseMtfAndroidBridge.getFeatureSe();
        if (null != featureSe && featureSe.size() > 0) {
            CodeInfo featureCode = FaceApiMtfV2Android.getInstance().matGetCodeInfo(MatType.NV21, nv21, width, height, codeInfo);
            if (null != featureCode && null != featureCode.getCode()) {
                CodeBean[] codeBeans = featureSe.searchCode(featureCode.getCode(), similarityThreshold, 1, null, 0);
                if (null != codeBeans && codeBeans.length > 0) {
                    CodeBean codeBean = codeBeans[0];
                    mLog("searchCode: " + codeBean.toString());
//                    mToast("人脸识别成功： " + codeBean.appIntId());
                    handler.obtainMessage(LABEL_SHOW_RESULT, "人脸识别成功: " + codeBean.appIntId()).sendToTarget();
                }
            }
        }
        return false;
    }


    /**
     * 判断当前是否为活体
     *
     * @param codeInfo
     * @param nv21
     * @param width
     * @param height
     * @return true是活体  false非活体
     */
    private boolean liveDetect(CodeInfo codeInfo, byte[] nv21, int width, int height) {
        try {
            /*活体检测*/
            FRect pos = codeInfo.getPos();
            /*人脸检测数据的(double)数组长度  对应mtf的*/
            double[] faceDou = new double[21];
            faceDou[0] = pos.getLeft();
            faceDou[1] = pos.getTop();
            faceDou[2] = pos.getWidth();
            faceDou[3] = pos.getHeight();
            faceDou[4] = 0.91;
            /*活体等级 默认*/
            faceDou[faceDou.length - 1] = 2;

            timeLive = System.currentTimeMillis();
            AtomicReference<Double> doubleAtomicReference = new AtomicReference<>();
            int liveProcess = FaceLiveAndroid.getInstance().nativeLiveProcess(MatType.NV21, nv21, width, height, faceDou, doubleAtomicReference);
            String format = String.format("live=%s    source=%s   time=%s", liveProcess, df.format(doubleAtomicReference.get()), (System.currentTimeMillis() - timeLive));
            LogUtils.file("faceSDK", format);
            mLog(format);
            if (0 == liveProcess) {
//                showResult("非活体", false);
                return false;
            } else if (1 == liveProcess) {
                showResult("活体  true ", false);
                return true;
            }
            /*return 1 == liveProcess;*/
        } catch (Exception e) {
            mLog(String.format("live= %s", e.getMessage()));
            e.printStackTrace();
        }
        return false;
    }

    private long timeLive = 0;/*记录单次活体检测的时间*/
    private long timeFeature = 0;/*记录单次特征提取的时间*/
    private long timeRecognition = 0;/*记录识别成功所用的所有时间 , 起始时间为注册成功之后*/


    private volatile boolean statusThread = true;
    DecimalFormat df = new DecimalFormat("0.000");


    private void featureAndLive(CodeInfo codeInfo, final byte[] nv21, int width, int height) {
        if (statusThread/* && null != getFeature()*/) {/*添加判空，再确认有注册特征码之后才进行下一步操作*/
            statusThread = false;
            ThreadUtils.getSinglePool().submit(() -> {
                try {
                    mLog("featureAndLive -----------------------start");
                    //是否使用活体检测
                    if (liveStatus) {
                        boolean liveDetect = liveDetect(codeInfo, nv21, width, height);
                        if (liveDetect) {
//                                boolean compare = compareFeature(codeInfo, nv21, width, height);
                            boolean compare = compareFeatureSE(codeInfo, nv21, width, height);
                            if (compare) {
                                showResult("人脸活体识别成功 " /*+ df.format(compare)*/, false);
                            }
                        }
                    } else {
//                            boolean compare = compareFeature(codeInfo, nv21, width, height);
                        boolean compare = compareFeatureSE(codeInfo, nv21, width, height);
                        if (compare) {
                            showResult("人脸识别成功 "/* + df.format(compare)*/, false);
                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    statusThread = true;
                    mLog("featureAndLive ----------------------- end");
                }
            });
        }
    }

    private void showResult(String con, boolean status) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tv.setText(con);
                handler.obtainMessage(LABEL_SHOW, status ? 0 : 1, 0, con).sendToTarget();
            }
        });
    }

    private final int LABEL_SHOW = 0x80;
    private final int LABEL_FINISH = 0x81;
    private final int LABEL_TASK = 0x82;
    private final int LABEL_TASK_RESTART = 0x83;
    private final int LABEL_COUNT_DOWN = 0x84;
    private final int LABEL_SHOW_RESULT = 0x85;
    @SuppressLint("HandlerLeak")
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case LABEL_SHOW:
                    if (msg.arg1 == 1) {
                        handler.removeMessages(LABEL_SHOW);
                        handler.sendEmptyMessageDelayed(LABEL_SHOW, 2500);
                    }
                    if (null == msg.obj) {
                        tv.setVisibility(View.GONE);
                    } else {
                        tv.setVisibility(View.VISIBLE);
                    }
                    tv.setText(String.valueOf(msg.obj));
                    break;
                case LABEL_FINISH:
                    FaceActivity.this.finish();
                    break;
                case LABEL_TASK:
                    localRegisterFace(path);
                    break;
                case LABEL_TASK_RESTART:
                    if (null != camera1TextureView) {
                        camera1TextureView.stopPreview();
                    }
                    LogUtils.file("faceSDK", "+++++++++++++++++++++++++++++++++++++++++++++++++++++");
                    startPreview();
                    break;
                case LABEL_COUNT_DOWN:
                    handler.removeMessages(LABEL_COUNT_DOWN);
                    if (sumCountDown > 0 && sumCountDown <= 30) {
                        sumCountDown--;
                        tvCountDown.setText("" + sumCountDown);
                        handler.sendEmptyMessageDelayed(LABEL_COUNT_DOWN, 1000);
                    } else {
                        handler.sendEmptyMessage(LABEL_FINISH);
                        LogUtils.file("activity", "倒计时到了退出  " + sumCountDown);
                    }
                    break;
                case LABEL_SHOW_RESULT:
                    Object obj = msg.obj;
                    if (null == obj) {
                        tvResult.setVisibility(View.GONE);
                    } else {
                        handler.removeMessages(LABEL_SHOW_RESULT);
                        tvResult.setVisibility(View.VISIBLE);
                        tvResult.setText(String.valueOf(msg.obj));
                        handler.sendEmptyMessageDelayed(LABEL_SHOW_RESULT, 2500);
                    }
                    break;
                default:
                    break;
            }
        }
    };

    String path = "/mnt/sdcard/Android/data/com.hww.dly_demo/files/test.jpg";
    //    String path = "/data/user/0/com.hww.dly_demo/files/test.jpg";
//    String path = "/sdcard/test.jpg";
    View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.test_btn_back:
                    FaceActivity.this.finish();
                    break;
                case R.id.test_btn_local:
                    if (null == getFeature()) {
                        localRegisterFace(path);
                    } else {
                        showResult(path + "\n已经注册成功", true);
                    }
                    break;
                case R.id.test_btn_live:
                    showLiveStatus();
                    liveStatus = !liveStatus;
                    break;
                default:
                    break;
            }
        }
    };

    private void showLiveStatus() {
        if (null != btLive) {
            if (liveStatus) {
                btLive.setText("活体:关");
            } else {
                btLive.setText("活体:开");
            }
        }
    }


    private void localRegisterFace(final String path) {
//        MyApp.SINGLE_EXECUTOR.submit(() -> {
//            mLog("localRegisterFace   start");
//            Bitmap bitmap = ImageUtils.getBitmap(path);
//            if (null == bitmap) {
//                showResult(path + "\n获取照片失败", true);
//                return;
//            }
//            try {
//                LogUtils.file("faceSDK", "register = " + "本地文件读取成功");
//                CodeInfo[] codeInfos = FaceApiMtfV2Android.getInstance().detectAndGetCodeInfo(bitmap, 0);
//                if (null != codeInfos && codeInfos.length > 0) {
//                    showResult("照片注册成功", true);
//                    setFeature(codeInfos[0].getCode());
//                    LogUtils.file("faceSDK", "register = 照片注册成功");
//                } else {
//                    showResult("照片注册失败", true);
//                    LogUtils.file("faceSDK", "register = 照片注册失败");
//                }
//            } catch (NotFaceDetectedException e) {
//                showResult("照片未检测到人脸", true);
//                LogUtils.file("faceSDK", "register = 照片未检测到人脸");
//            } catch (Exception e) {
//                LogUtils.file("faceSDK", "register = 检测异常");
//                showResult("检测异常:" + e.getLocalizedMessage(), true);
//                e.printStackTrace();
//            } finally {
//                mLog("localRegisterFace   finally");
//            }
//        });
    }


    private void mLog(String con) {
        Log.e("hwwFace", "" + con);
    }

    private void mToast(final String con) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(FaceActivity.this, con, Toast.LENGTH_SHORT).show();
            }
        });
    }

//    private void setV2DFLPoolConfig() {
//        LogUtils.file("activity", "setV2FLPoolConfig()");
//        int timeMaxWait = 5 * 1000;
//        int timeOut = 25 * 1000;
//
//        /*检测配置可用资源总数量*/
//        BaseFaceApiMtfAndroid.getPoolConfig().setMaxTotal(2);
//        /*配置资源排队时的最大等待时间*/
//        BaseFaceApiMtfAndroid.getPoolConfig().setMaxWaitMillis(timeMaxWait);
//        /*配置不可保留资源的回收超时时间*/
//        BaseFaceApiMtfAndroid.getPoolConfig().setMinEvictableIdleTimeMillis(timeOut);
//        /*配置最大可以保留的ID(资源)数量*/
//        BaseFaceApiMtfAndroid.getPoolConfig().setMaxIdle(1);
//
//
//        /*AtomicLong timeF = new AtomicLong();
//        FaceApiMtfV2Android.INSTANCE.testV2Init(timeF);
//        long f = timeF.get();*/
//        /*检测配置可用资源总数量*/
//        FaceApiMtfV2Android.getPoolConfig().setMaxTotal(2);
//        /*配置资源排队时的最大等待时间*/
//        FaceApiMtfV2Android.getPoolConfig().setMaxWaitMillis(timeMaxWait);
//        /*配置不可保留资源的回收超时时间*/
//        FaceApiMtfV2Android.getPoolConfig().setMinEvictableIdleTimeMillis(timeOut);
//        /*配置最大可以保留的ID(资源)数量*/
//        FaceApiMtfV2Android.getPoolConfig().setMaxIdle(1);
//
////        AtomicLong timeL = new AtomicLong();
////        FaceLiveAndroidFacelibImpl.INSTANCE.testInit(timeL);
////        long l = timeL.get();
//        FaceLiveAndroidFacelibImpl.getPoolConfig().setMaxTotal(1);
//        FaceLiveAndroidFacelibImpl.getPoolConfig().setMaxWaitMillis(timeMaxWait);
//        FaceLiveAndroidFacelibImpl.getPoolConfig().setMinEvictableIdleTimeMillis(timeOut);
//        FaceLiveAndroidFacelibImpl.getPoolConfig().setMaxIdle(1);
//    }

}
